'vscode 插件下载'
# 提供publisher.extname 和 版本参数
# 保存路径--path=? 可选,默认当前文件夹
from concurrent.futures import ThreadPoolExecutor, as_completed
from configparser import ConfigParser
from os import getcwd, listdir, makedirs
from os.path import exists, join, split, splitext
from shutil import rmtree
from time import asctime

import requests
from bs4 import BeautifulSoup

cfg = ConfigParser()
cfg.read('config.ini')


def get_ve_info(ItemName):
    '获取vscode ext 版本和下载地址'
    url = 'https://marketplace.visualstudio.com/items?itemName={}'.format(
        ItemName)
    htm = requests.get(url, verify=False)
    if htm.ok:
        try:
            bs = BeautifulSoup(htm.content, 'html.parser')
            _ = bs.select('div .vss-item-data')
            if not _:
                return
            data = eval(_[0].text.strip())
            version = data['versions'][0]['version']
            d_url = data['versions'][0]['files'][-1]['source']
        except:
            print('{} 页面数据解析失败!'.format(ItemName))
            return
        else:
            return {ItemName: (version, d_url)}
    else:
        return


def download_vscode(sp):
    '下载vscode.exe程序'
    url = 'https://code.visualstudio.com/'
    url_32 = 'https://vscode-update.azurewebsites.net/latest/win32/stable'
    url_64 = 'https://vscode-update.azurewebsites.net/latest/win32-x64/stable'
    # get current version
    v = cfg.get('vscode', 'version')
    # get last version
    h = requests.get(url, verify=False)
    if not h.ok:
        return
    bs = BeautifulSoup(h.content, 'html.parser')
    last_version = bs.select(
        'a#banner-link-updates')[0].text.split()[-1].strip()
    # get current version
    vs = [i for i in listdir(sp) if i in ('vscode_32.exe', 'vscode_64.exe')]
    down = False if len(vs) == 2 and v == last_version else True
    if not down:
        return
    with ThreadPoolExecutor() as pool:
        for r in as_completed(pool.submit(download_file, sp, *i) for i in [('vscode_32', url_32), ('vscode_64', url_64)]):
            r.result()
    cfg.set('vscode', 'version', last_version)


def get_cfg(cp):
    '获取配置信息'
    # save path 未配置默认当前目录下exts文件夹
    cfg = ConfigParser()
    cfg.read(join(cp, 'config.ini'))
    if not cfg:
        print('不存在config.ini配置文件!')
        return
    try:
        sp = cfg.items('save path')
        exts = dict(cfg.items('vscode extentions'))
        vs_version = cfg.items('vscode')[0][-1]
        # name:(itemName,version)
        exts = {k.split('.')[-1]: (k, v) for k, v in exts.items()}
        if not sp:
            sp = join(cp, 'exts')
        if not exists(sp):
            makedirs(sp)
        return sp, exts, vs_version
    except:
        print('配置节点错误!')
        return


def get_require_update_exts(sp):
    '获取需要更新的exts地址'
    # 版本默认cfg里面信息
    exts = dict(cfg.items('extentions'))
    # ItemName:(version,d_url)
    new_exts = {}
    with ThreadPoolExecutor() as pool:
        for r in as_completed(pool.submit(get_ve_info, k) for k in exts.keys()):
            new_exts.update(r.result())
    # get has exts
    # ext_names
    cur_exts = tuple(splitext(i)[0] for i in listdir(sp))
    cur_exts = {k: v for k, v in exts.items() if k.endswith(cur_exts)}
    for k, v in cur_exts.items():
        nv = new_exts[k][0]
        if v == nv:
            del new_exts[k]
        else:
            cfg.set('extentions', k, nv)
    return ((k, v[-1]) for k, v in new_exts.items())


def download_file(sp, ItemName, url):
    c = requests.get(url, verify=False)
    if url.endswith('stable'):
        ext = 'exe'
    else:
        ext = 'vsix'
    if c.ok:
        with open(join(sp, '{}.{}'.format(ItemName.split('.')[-1], ext)), 'wb') as f:
            f.write(c.content)
        with open(join(sp, 'news', '{}.{}'.format(ItemName.split('.')[-1], ext)), 'wb') as f:
            f.write(c.content)

    else:
        cg = ConfigParser()
        cg.read('config.ini')
        if ext == 'exe':
            cfg.set('vscode', 'version', cg.get('vscode', 'version'))
        else:
            cfg.set('extentions', ItemName, cg.get('extentions', ItemName))
        print('{}下载失败!'.format(ItemName))


def fetch_vs_exts():
    '多线程'
    # 默认当前目录下exts文件夹
    sp = cfg.get('save', 'path')
    if not exists(sp):
        sp = join(getcwd(), 'exts')
        if not exists(sp):
            makedirs(sp)
    news = join(sp, 'news')
    if exists(news):
        rmtree(news)
        makedirs(news)
    else:
        makedirs(news)
    # 下载vscode.exe
    download_vscode(sp)
    # 下载扩展
    exts = get_require_update_exts(sp)
    with ThreadPoolExecutor() as pool:
        for r in as_completed(pool.submit(download_file, sp, *i) for i in exts):
            r.result()
    # save config.ini
    with open('config.ini', 'w') as f:
        cfg.write(f)


if __name__ == '__main__':
    print('update start', asctime())
    fetch_vs_exts()
    print('end', asctime())
